/**
 * @brief Functions for communicating with the client.
 *
 * @file communicator.c
 *
 * @author Robert Breznak, Peter Kalauskas
 */

#include "communicator.h"
#include "serial.h"
#include "dispatcher.h"

/**
 * The buffer for storing incoming transmissions.
 */
uint8_t buffer[MAX_MESSAGE_LENGTH];
/**
 * The expected length of the transmission.  This will be set to INVALID_LENGTH
 * by default.
 */
int8_t transmission_length;
/**
 * The current index of the buffer.
 */
int8_t buffer_index;

/**
 * @fn void init_communicator()
 *
 * Initializes the communicator.
 *
 * @author Robert Breznak, Peter Kalauskas
 */
void init_communicator() {
	init_serial();
	reset_transmission_record();
}

/**
 * @fn void send_command(uint8_t device, uint8_t id, uint8_t command, uint8_t *data, uint8_t data_length)
 *
 * Sends a function and data to the given device on the client.  The client will
 * interpret this as a Message object and pass it along to the correct device.
 *
 * @author Robert Breznak, Peter Kalauskas
 */
void send_command(uint8_t device, uint8_t id, uint8_t command, uint8_t *data,
		uint8_t data_length) {
	int length = 3 + data_length;
	uint8_t *info = malloc(length * sizeof(uint8_t));
	info[0] = device;
	info[1] = id;
	info[2] = command;
	int i;
	for (i = 3; i < length; i++) {
		info[i] = data[i - 3];
	}
	// Follow the protocol and send a KNOCK byte, the length of the bytes to
	// follow and then the bytes which make up a message.
	send_byte(KNOCK);
	send_byte(length);
	send_bytes(info, length);

	free(info);
}

/**
 * @fn void reset_transmission_record()
 *
 * Clears the buffer and resets the index to 0.
 *
 * @author Robert Breznak, Peter Kalauskas
 */
void reset_transmission_record() {
	int i;
	for (i = 0; i < MAX_MESSAGE_LENGTH; i++) {
		buffer[i] = 0;
	}
	transmission_length = INVALID_LENGTH;
	buffer_index = BUFFER_PARKED;
}

/**
 * @fn uint8_t add_byte_to_transmission_record(uint8_t byte)
 * Adds a byte to the transmission buffer.  The byte's role in the transmission
 * will be determined by the function.  The byte may be invalidly placed (ie. it
 * may not be the starting byte where a starting should be), a length byte
 * (which is not added to the buffer), or it may be added to the buffer.
 *
 * @param byte The byte to be added to the transmission.
 * @return uint8_t 0 if this is the end of the transmission, 1 otherwise
 * @author Peter Kalauskas
 */
uint8_t add_byte_to_transmission_record(uint8_t byte) {

	// make sure the first byte received is a KNOCK.  This is to
	// help reduce the possibility that garbage initiates a
	// transmission
	if (buffer_index == BUFFER_PARKED && byte == KNOCK) {
		buffer_index = BUFFER_AWAITING_LENGTH;
	} else if (buffer_index != BUFFER_PARKED) {
		// Make sure the buffer is not parked.  A parked buffer cannot be written to.

		// Prevent buffer overflows by preventing any more additions to the buffer.
		if (buffer_index < MAX_MESSAGE_LENGTH) {
			// if this is the byte which indicates length, set it as the length variable
			if (buffer_index == BUFFER_AWAITING_LENGTH) {
				transmission_length = byte;
				buffer_index = BUFFER_START;
			} else {
				// add the new byte to the buffer
				buffer[buffer_index++] = byte;
			}
		}
	}

	if (buffer_index == transmission_length) {
		return 1;
	} else {
		return 0;
	}
}

/**
 * The interrupt for receiving UART communications
 *
 * @return Nothing
 * @author Peter Kalauskas
 */
ISR(USART0_RX_vect)
{
	// get the data from the UART line
	while (!(UCSR0A & (1 << RXC0))) {
	}
	uint8_t byte = UDR0;

	if (add_byte_to_transmission_record(byte)) {
		// the transmission is complete, find the correct device and
		// dispatch the command.
		dispatch(buffer,buffer+3,buffer_index-3);
		reset_transmission_record();
	}
}
